package net.xiduth.game.npc;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;

import net.xiduth.Settings;
import net.xiduth.cache.loaders.ItemDefinitions;
import net.xiduth.cache.loaders.NPCDefinitions;
import net.xiduth.cores.CoresManager;
import net.xiduth.game.Animation;
import net.xiduth.game.Entity;
import net.xiduth.game.Graphics;
import net.xiduth.game.Hit;
import net.xiduth.game.Hit.HitLook;
import net.xiduth.game.World;
import net.xiduth.game.WorldTile;
import net.xiduth.game.item.Item;
import net.xiduth.game.npc.combat.NPCCombat;
import net.xiduth.game.npc.combat.NPCCombatDefinitions;
import net.xiduth.game.npc.familiar.Familiar;
import net.xiduth.game.player.Player;
import net.xiduth.game.player.Skills;
import net.xiduth.game.player.content.Burying;
import net.xiduth.game.player.content.FriendChatsManager;
import net.xiduth.game.player.controlers.WGuildControler;
import net.xiduth.game.player.controlers.Wilderness;
import net.xiduth.game.tasks.WorldTask;
import net.xiduth.game.tasks.WorldTasksManager;
import net.xiduth.utils.ChatColors;
import net.xiduth.utils.Logger;
import net.xiduth.utils.MapAreas;
import net.xiduth.utils.Utils;
import net.xiduth.utils.npc.NPCBonuses;
import net.xiduth.utils.npc.NPCCombatDefinitionsL;
import net.xiduth.utils.npc.NPCDrops;

public class NPC extends Entity implements Serializable {

	private static final long serialVersionUID = -4794678936277614443L;

	public static String[] GODWARS = { "6260", "6222", "6203", "6247" };
	
	private int id;
	private WorldTile respawnTile;
	private int mapAreaNameHash;
	private boolean canBeAttackFromOutOfArea;
	private boolean randomwalk;
	private int[] bonuses;
	private boolean spawned;
	private transient NPCCombat combat;
	public WorldTile forceWalk;

	private long lastAttackedByTarget;
	private boolean cantInteract;
	private int capDamage;
	private int lureDelay;
	private boolean cantFollowUnderCombat;
	private boolean forceAgressive;
	private int forceTargetDistance;
	private boolean forceFollowClose;
	private boolean forceMultiAttacked;
	private boolean noDistanceCheck;

	// npc masks
	private transient Transformation nextTransformation;
	// name changing masks
	private String name;
	private transient boolean changedName;
	private int combatLevel;
	private transient boolean changedCombatLevel;
	private transient boolean locked;
	public transient Player instancedPlayer;
	private transient TimerBar Timerbar;

	private static final Item[] CHARMS = {
			// Charms
			new Item(12158, 3), new Item(12159, 3), new Item(12160, 3),
			new Item(12163, 3), new Item(12168, 3),
			// Uncuts
			new Item(1625, 1), new Item(1623, 1), new Item(1621, 1),
			new Item(1619, 1), new Item(1631, 1),
			// Keys and stardust <3
			new Item(13727, Utils.random(5)), new Item(13727, Utils.random(5)) };

	private static boolean forceRandomWalk(int npcId) {
		switch (npcId) {
		case 11226:
			return true;
		case 3341:
		case 3342:
		case 3343:
			return true;
		default:
			return false;
		}
	}

	public NPC(int id, WorldTile tile, int mapAreaNameHash,
			boolean canBeAttackFromOutOfArea) {
		this(id, tile, mapAreaNameHash, canBeAttackFromOutOfArea, false);
	}

	/*
	 * creates and adds npc
	 */
	public NPC(int id, WorldTile tile, int mapAreaNameHash,
			boolean canBeAttackFromOutOfArea, boolean spawned) {
		super(tile);
		this.id = id;
		this.respawnTile = new WorldTile(tile);
		this.mapAreaNameHash = mapAreaNameHash;
		this.canBeAttackFromOutOfArea = canBeAttackFromOutOfArea;
		this.setSpawned(spawned);
		combatLevel = -1;
		setHitpoints(getMaxHitpoints());
		setDirection(getRespawnDirection());
		setRandomWalk((getDefinitions().walkMask & 0x2) != 0
				|| forceRandomWalk(id));
		bonuses = NPCBonuses.getBonuses(id);
		combat = new NPCCombat(this);
		capDamage = -1;
		lureDelay = 12000;
		initEntity();
		World.addNPC(this);
		World.updateEntityRegion(this);
		loadMapRegions();
		checkMultiArea();
	}

	public boolean canBeAttackedByAutoRelatie() {
		return Utils.currentTimeMillis() - lastAttackedByTarget > lureDelay;
	}

	public boolean canBeAttackFromOutOfArea() {
		return canBeAttackFromOutOfArea;
	}

	public boolean checkAgressivity() {
		// if(!(Wilderness.isAtWild(this) &&
		// getDefinitions().hasAttackOption())) {
		if (!forceAgressive) {
			NPCCombatDefinitions defs = getCombatDefinitions();
			if (defs.getAgressivenessType() == NPCCombatDefinitions.PASSIVE)
				return false;
		}
		// }
		ArrayList<Entity> possibleTarget = getPossibleTargets();
		if (!possibleTarget.isEmpty()) {
			Entity target = possibleTarget.get(Utils.random(possibleTarget
					.size()));
			setTarget(target);
			target.setAttackedBy(target);
			target.setFindTargetDelay(Utils.currentTimeMillis() + 10000);
			return true;
		}
		return false;
	}

	public void deserialize() {
		if (combat == null)
			combat = new NPCCombat(this);
		spawn();
	}

	public void drop() {
		try {
			Drop[] drops = NPCDrops.getDrops(id);
			if (drops == null)
				return;
			Player killer = getMostDamageReceivedSourcePlayer();
			if (killer == null)
				return;
			/*if (killer.ToggleSystem == true) {
			 if (this.getId() == 6270) { 
	                killer.setBandos(killer.getBandos() + 1);
	                killer.sm("You successfully killed this npc your Bonus Task.");
	         }
			 killer.rankpoints += 2;
			// killer.ranks += 1;
			 Ranks.checkRank(killer);
			
			 if (killer.bandos == 100) {
				 killer.setSpins(killer.getSpins() + 5);
				 killer.sm("You where awarded 5 SOF spins for completing this bonus task.");
				 killer.getBank().addItem(995, 5000000, true);
				 killer.sm("You where awarded 5,000,000 GP for completing this bonus task.");
				 World.sendWorldMessage("<img=6><col=FF000>"+ killer.getDisplayName() +" has completed the Bonus Task!", false);
			 }
			}*/
			ArrayList<Integer> saradomin = new ArrayList<Integer>();
			saradomin.add(6254);
			saradomin.add(6259);
			saradomin.add(6258);
			saradomin.add(6212);
			saradomin.add(6257);
			saradomin.add(6256);
			saradomin.add(6253);
			saradomin.add(3406);
			saradomin.add(6248);
			saradomin.add(6247);
			saradomin.add(6252);
			saradomin.add(6255);
						if (saradomin.indexOf(getId()) != -1) {
				killer.setSaradominKC(killer.getSaradominKC() + 1);
				killer.getInterfaceManager().updateGWDKC();
			}
			ArrayList<Integer> armadyl = new ArrayList<Integer>();
			armadyl.add(6246);
			armadyl.add(6236);
			armadyl.add(6232);
			armadyl.add(6240);
			armadyl.add(6241);
			armadyl.add(6242);
			armadyl.add(6235);
			armadyl.add(6234);
			armadyl.add(6243);
			armadyl.add(6236);
			armadyl.add(6244);
			armadyl.add(6237);
			armadyl.add(6246);
			armadyl.add(6238);
			armadyl.add(6239);
			armadyl.add(6230);
			if (armadyl.indexOf(getId()) != -1) {
				killer.setArmadylKC(killer.getArmadylKC() + 1);
				killer.getInterfaceManager().updateGWDKC();

			}
			ArrayList<Integer> bandos = new ArrayList<Integer>();
			bandos.add(6281);
			bandos.add(6282);
			bandos.add(6275);
			bandos.add(6279);
			bandos.add(9184);
			bandos.add(6268);
			bandos.add(6269);
			bandos.add(6270);
			bandos.add(6274);
			bandos.add(6277);
			bandos.add(6276);
			bandos.add(6278);
			bandos.add(6272);
			if (bandos.indexOf(getId()) != -1) {
				killer.setBandos(killer.getBandos() + 1);
				killer.getInterfaceManager().updateGWDKC();
			}
			ArrayList<Integer> zamorak = new ArrayList<Integer>();
			zamorak.add(6211);
			zamorak.add(3406);
			zamorak.add(6216);
			zamorak.add(6214);
			zamorak.add(6215);
			zamorak.add(6212);
			zamorak.add(6219);
			zamorak.add(6221);
			zamorak.add(6218);
			if (zamorak.indexOf(getId()) != -1) {
				killer.setZamorakKC(killer.getZamorakKC() + 1);
				killer.getInterfaceManager().updateGWDKC();
			}
			if (this.getId() == 9177) {
				killer.sm("You have received bonus PvM points!");
				killer.setPvMPoints(killer.getPvMPoints() + 250);
			}
			if (killer.getTask() != null) {
				if (getDefinitions().name.toLowerCase().equalsIgnoreCase(
						killer.getTask().getName().toLowerCase())) {
					killer.getSkills().addXp(Skills.SLAYER,
							killer.getTask().getXPAmount());
					killer.getTask().decreaseAmount();
					if (killer.getTask().getTaskAmount() < 1) {
						if (killer.getEquipment().getRingId() == 13281) {
							killer.setSlayerPoints(killer.getSlayerPoints() + 40);
							killer.getPackets()
									.sendGameMessage(
											"You have finished your slayer task, talk to Kuradal for a new task.");
							killer.getPackets().sendGameMessage(
									"Kuradal rewarded you 40 Slayer points! You now have "
											+ killer.getSlayerPoints()
											+ " slayerPoints.");
						} else {
							killer.setSlayerPoints(killer.getSlayerPoints() + 20);
							killer.getPackets()
									.sendGameMessage(
											"You have finished your slayer task, talk to Kuradal for a new task.");
							killer.getPackets().sendGameMessage(
									"Kuradal rewarded you 20 slayerPoints! You now have "
											+ killer.getSlayerPoints()
											+ " slayerPoints.");
						}
						killer.setTask(null);
						return;
					}
					killer.getTask().setAmountKilled(
							killer.getTask().getAmountKilled() + 1);
					killer.getPackets().sendGameMessage(
							"You need to defeat "
									+ killer.getTask().getTaskAmount() + " "
									+ killer.getTask().getName().toLowerCase()
									+ " to complete your task.");
				}
	
			}
			Drop[] possibleDrops = new Drop[drops.length];
			int possibleDropsCount = 0;
			for (Drop drop : drops) {
				Item item = ItemDefinitions
						.getItemDefinitions(drop.getItemId()).isStackable() ? new Item(
						drop.getItemId(),
						(drop.getMinAmount() * Settings.DROP_RATE)
								+ Utils.getRandom(drop.getExtraAmount()
										* Settings.DROP_RATE)) : new Item(
						drop.getItemId(), drop.getMinAmount()
								+ Utils.getRandom(drop.getExtraAmount()));

				//if (killer.getEquipment().getRingId() == 2572)
				//	Utils.getRandomDouble(drop.getExtraAmount()
					//		* Settings.DROP_RATE * 2);
					//sendDrop(killer, drop); dont senddrop meh

				if (killer.getInventory().containsItem(18337, 1)
						&& item.getDefinitions().getName().toLowerCase()
								.contains("bones")) {
					killer.getSkills().addXp(
							Skills.PRAYER,
							Burying.Bone.forId(drop.getItemId())
									.getExperience());
					continue;
				}
				if (drop.getRate() == 100)
					sendDrop(killer, drop);
				else {
					if ((Utils.getRandomDouble(99) + 1) <= drop.getRate() * 1.0)
						possibleDrops[possibleDropsCount++] = drop;
				}
			}
			if (possibleDropsCount > 0)
				sendDrop(killer,
						possibleDrops[Utils.getRandom(possibleDropsCount - 1)]);
		} catch (Exception e) {
			e.printStackTrace();
		} catch (Error e) {
			e.printStackTrace();
		}
	}

	public void dropCharm(Player player, Item item) {
		int size = getSize();
		Item dropItem = new Item(item.getId(), Utils.random(item
				.getDefinitions().isStackable() ? item.getAmount()
				* Settings.DROP_RATE : item.getAmount()) + 1);

		World.addGroundItem(dropItem, new WorldTile(getCoordFaceX(size),
				getCoordFaceY(size), getPlane()), player, false, 180, true);

	}

	@Override
	public void finish() {
		if (hasFinished())
			return;
		setFinished(true);
		World.updateEntityRegion(this);
		World.removeNPC(this);
	}

	public void forceWalkRespawnTile() {
		setForceWalk(respawnTile);
	}

	public int[] getBonuses() {
		return bonuses;
	}

	public int getCapDamage() {
		return capDamage;
	}

	public NPCCombat getCombat() {
		return combat;
	}

	public NPCCombatDefinitions getCombatDefinitions() {
		return NPCCombatDefinitionsL.getNPCCombatDefinitions(id);
	}

	public int getCombatLevel() {
		return combatLevel >= 0 ? combatLevel : getDefinitions().combatLevel;
	}

	public int getCustomCombatLevel() {
		return combatLevel;
	}

	public String getCustomName() {
		return name;
	}

	public NPCDefinitions getDefinitions() {
		return NPCDefinitions.getNPCDefinitions(id);
	}

	public int getForceTargetDistance() {
		return forceTargetDistance;
	}

	public int getId() {
		return id;
	}

	public int getLureDelay() {
		return lureDelay;
	}

	@Override
	public double getMagePrayerMultiplier() {
		return 0;
	}

	public int getMapAreaNameHash() {
		return mapAreaNameHash;
	}

	public int getMaxHit() {
		return getCombatDefinitions().getMaxHit();
	}

	@Override
	public int getMaxHitpoints() {
		return getCombatDefinitions().getHitpoints();
	}

	@Override
	public double getMeleePrayerMultiplier() {
		return 0;
	}

	public WorldTile getMiddleWorldTile() {
		int size = getSize();
		return new WorldTile(getCoordFaceX(size), getCoordFaceY(size),
				getPlane());
	}

	public String getName() {
		return name != null ? name : getDefinitions().name;
	}

	public TimerBar getNextTimerbar() {
		return Timerbar;
	}

	public Transformation getNextTransformation() {
		return nextTransformation;
	}

	public ArrayList<Entity> getPossibleTargets() {
		ArrayList<Entity> possibleTarget = new ArrayList<Entity>();
		for (int regionId : getMapRegionsIds()) {
			List<Integer> playerIndexes = World.getRegion(regionId)
					.getPlayerIndexes();
			if (playerIndexes != null) {
				for (int playerIndex : playerIndexes) {
					Player player = World.getPlayers().get(playerIndex);
					if (instancedPlayer != null && instancedPlayer != player) {
						continue;
					}
					if (player == null
							|| player.isDead()
							|| player.hasFinished()
							|| !player.isRunning()
							|| !player
									.withinDistance(
											this,
											forceTargetDistance > 0 ? forceTargetDistance
													: (getCombatDefinitions()
															.getAttackStyle() == NPCCombatDefinitions.MELEE ? 4
															: getCombatDefinitions()
																	.getAttackStyle() == NPCCombatDefinitions.SPECIAL ? 64
																	: 8))
							|| (!forceMultiAttacked
									&& (!isAtMultiArea() || !player
											.isAtMultiArea())
									&& player.getAttackedBy() != this && (player
									.getAttackedByDelay() > Utils
									.currentTimeMillis() || player
									.getFindTargetDelay() > Utils
									.currentTimeMillis()))
							|| !clipedProjectile(player, false)
							|| (!forceAgressive && !Wilderness.isAtWild(this) && player
									.getSkills().getCombatLevelWithSummoning() >= getCombatLevel() * 2))
						continue;
					possibleTarget.add(player);
				}
			}
		}
		return possibleTarget;
	}

	@Override
	public double getRangePrayerMultiplier() {
		return 0;
	}

	public int getRespawnDirection() {
		NPCDefinitions definitions = getDefinitions();
		if (definitions.anInt853 << 32 != 0 && definitions.respawnDirection > 0
				&& definitions.respawnDirection <= 8)
			return (4 + definitions.respawnDirection) << 11;
		return 0;
	}

	public WorldTile getRespawnTile() {
		return respawnTile;
	}

	@Override
	public int getSize() {
		return getDefinitions().size;
	}

	@Override
	public void handleIngoingHit(final Hit hit) {
		if (capDamage != -1 && hit.getDamage() > capDamage)
			hit.setDamage(capDamage);
		if (hit.getLook() != HitLook.MELEE_DAMAGE
				&& hit.getLook() != HitLook.RANGE_DAMAGE
				&& hit.getLook() != HitLook.MAGIC_DAMAGE)
			return;
		Entity source = hit.getSource();
		if (source == null)
			return;
		if (source instanceof Player) {
			final Player p2 = (Player) source;
			if (p2.getPrayer().hasPrayersOn()) {
				if (p2.getPrayer().usingPrayer(1, 18))
					sendSoulSplit(hit, p2);
				if (hit.getDamage() == 0)
					return;
				if (!p2.getPrayer().isBoostedLeech()) {
					if (hit.getLook() == HitLook.MELEE_DAMAGE) {
						if (p2.getPrayer().usingPrayer(1, 19)) {
							p2.getPrayer().setBoostedLeech(true);
							return;
						} else if (p2.getPrayer().usingPrayer(1, 1)) { // sap
							if (Utils.getRandom(4) == 0) {
								if (p2.getPrayer().reachedMax(0)) {
									p2.getPackets()
											.sendGameMessage(
													"Your opponent has been weakened so much that your sap curse has no effect.",
													true);
								} else {
									p2.getPrayer().increaseLeechBonus(0);
									p2.getPackets()
											.sendGameMessage(
													"Your curse drains Attack from the enemy, boosting your Attack.",
													true);
								}
								p2.setNextAnimation(new Animation(12569));
								p2.setNextGraphics(new Graphics(2214));
								p2.getPrayer().setBoostedLeech(true);
								World.sendProjectile(p2, this, 2215, 35, 35,
										20, 5, 0, 0);
								WorldTasksManager.schedule(new WorldTask() {
									@Override
									public void run() {
										setNextGraphics(new Graphics(2216));
									}
								}, 1);
								return;
							}
						} else {
							if (p2.getPrayer().usingPrayer(1, 10)) {
								if (Utils.getRandom(7) == 0) {
									if (p2.getPrayer().reachedMax(3)) {
										p2.getPackets()
												.sendGameMessage(
														"Your opponent has been weakened so much that your leech curse has no effect.",
														true);
									} else {
										p2.getPrayer().increaseLeechBonus(3);
										p2.getPackets()
												.sendGameMessage(
														"Your curse drains Attack from the enemy, boosting your Attack.",
														true);
									}
									p2.setNextAnimation(new Animation(12575));
									p2.getPrayer().setBoostedLeech(true);
									World.sendProjectile(p2, this, 2231, 35,
											35, 20, 5, 0, 0);
									WorldTasksManager.schedule(new WorldTask() {
										@Override
										public void run() {
											setNextGraphics(new Graphics(2232));
										}
									}, 1);
									return;
								}
							}
							if (p2.getPrayer().usingPrayer(1, 14)) {
								if (Utils.getRandom(7) == 0) {
									if (p2.getPrayer().reachedMax(7)) {
										p2.getPackets()
												.sendGameMessage(
														"Your opponent has been weakened so much that your leech curse has no effect.",
														true);
									} else {
										p2.getPrayer().increaseLeechBonus(7);
										p2.getPackets()
												.sendGameMessage(
														"Your curse drains Strength from the enemy, boosting your Strength.",
														true);
									}
									p2.setNextAnimation(new Animation(12575));
									p2.getPrayer().setBoostedLeech(true);
									World.sendProjectile(p2, this, 2248, 35,
											35, 20, 5, 0, 0);
									WorldTasksManager.schedule(new WorldTask() {
										@Override
										public void run() {
											setNextGraphics(new Graphics(2250));
										}
									}, 1);
									return;
								}
							}

						}
					}
					if (hit.getLook() == HitLook.RANGE_DAMAGE) {
						if (p2.getPrayer().usingPrayer(1, 2)) {
							if (Utils.getRandom(4) == 0) {
								if (p2.getPrayer().reachedMax(1)) {
									p2.getPackets()
											.sendGameMessage(
													"Your opponent has been weakened so much that your sap curse has no effect.",
													true);
								} else {
									p2.getPrayer().increaseLeechBonus(1);
									p2.getPackets()
											.sendGameMessage(
													"Your curse drains Range from the enemy, boosting your Range.",
													true);
								}
								p2.setNextAnimation(new Animation(12569));
								p2.setNextGraphics(new Graphics(2217));
								p2.getPrayer().setBoostedLeech(true);
								World.sendProjectile(p2, this, 2218, 35, 35,
										20, 5, 0, 0);
								WorldTasksManager.schedule(new WorldTask() {
									@Override
									public void run() {
										setNextGraphics(new Graphics(2219));
									}
								}, 1);
								return;
							}
						} else if (p2.getPrayer().usingPrayer(1, 11)) {
							if (Utils.getRandom(7) == 0) {
								if (p2.getPrayer().reachedMax(4)) {
									p2.getPackets()
											.sendGameMessage(
													"Your opponent has been weakened so much that your leech curse has no effect.",
													true);
								} else {
									p2.getPrayer().increaseLeechBonus(4);
									p2.getPackets()
											.sendGameMessage(
													"Your curse drains Range from the enemy, boosting your Range.",
													true);
								}
								p2.setNextAnimation(new Animation(12575));
								p2.getPrayer().setBoostedLeech(true);
								World.sendProjectile(p2, this, 2236, 35, 35,
										20, 5, 0, 0);
								WorldTasksManager.schedule(new WorldTask() {
									@Override
									public void run() {
										setNextGraphics(new Graphics(2238));
									}
								});
								return;
							}
						}
					}
					if (hit.getLook() == HitLook.MAGIC_DAMAGE) {
						if (p2.getPrayer().usingPrayer(1, 3)) {
							if (Utils.getRandom(4) == 0) {
								if (p2.getPrayer().reachedMax(2)) {
									p2.getPackets()
											.sendGameMessage(
													"Your opponent has been weakened so much that your sap curse has no effect.",
													true);
								} else {
									p2.getPrayer().increaseLeechBonus(2);
									p2.getPackets()
											.sendGameMessage(
													"Your curse drains Magic from the enemy, boosting your Magic.",
													true);
								}
								p2.setNextAnimation(new Animation(12569));
								p2.setNextGraphics(new Graphics(2220));
								p2.getPrayer().setBoostedLeech(true);
								World.sendProjectile(p2, this, 2221, 35, 35,
										20, 5, 0, 0);
								WorldTasksManager.schedule(new WorldTask() {
									@Override
									public void run() {
										setNextGraphics(new Graphics(2222));
									}
								}, 1);
								return;
							}
						} else if (p2.getPrayer().usingPrayer(1, 12)) {
							if (Utils.getRandom(7) == 0) {
								if (p2.getPrayer().reachedMax(5)) {
									p2.getPackets()
											.sendGameMessage(
													"Your opponent has been weakened so much that your leech curse has no effect.",
													true);
								} else {
									p2.getPrayer().increaseLeechBonus(5);
									p2.getPackets()
											.sendGameMessage(
													"Your curse drains Magic from the enemy, boosting your Magic.",
													true);
								}
								p2.setNextAnimation(new Animation(12575));
								p2.getPrayer().setBoostedLeech(true);
								World.sendProjectile(p2, this, 2240, 35, 35,
										20, 5, 0, 0);
								WorldTasksManager.schedule(new WorldTask() {
									@Override
									public void run() {
										setNextGraphics(new Graphics(2242));
									}
								}, 1);
								return;
							}
						}
					}

					// overall

					if (p2.getPrayer().usingPrayer(1, 13)) {
						if (Utils.getRandom(10) == 0) {
							if (p2.getPrayer().reachedMax(6)) {
								p2.getPackets()
										.sendGameMessage(
												"Your opponent has been weakened so much that your leech curse has no effect.",
												true);
							} else {
								p2.getPrayer().increaseLeechBonus(6);
								p2.getPackets()
										.sendGameMessage(
												"Your curse drains Defence from the enemy, boosting your Defence.",
												true);
							}
							p2.setNextAnimation(new Animation(12575));
							p2.getPrayer().setBoostedLeech(true);
							World.sendProjectile(p2, this, 2244, 35, 35, 20, 5,
									0, 0);
							WorldTasksManager.schedule(new WorldTask() {
								@Override
								public void run() {
									setNextGraphics(new Graphics(2246));
								}
							}, 1);
							return;
						}
					}
				}
			}
		}

	}

	public boolean hasChangedCombatLevel() {
		return changedCombatLevel;
	}

	public boolean hasChangedName() {
		return changedName;
	}

	public boolean hasForceWalk() {
		return forceWalk != null;
	}

	public boolean hasRandomWalk() {
		return randomwalk;
	}

	public boolean isCantFollowUnderCombat() {
		return cantFollowUnderCombat;
	}

	public boolean isCantInteract() {
		return cantInteract;
	}

	public boolean isForceAgressive() {
		return forceAgressive;
	}

	public boolean isForceFollowClose() {
		return forceFollowClose;
	}

	public boolean isForceMultiAttacked() {
		return forceMultiAttacked;
	}

	public boolean isForceWalking() {
		return forceWalk != null;
	}

	/**
	 * Gets the locked.
	 * 
	 * @return The locked.
	 */
	public boolean isLocked() {
		return locked;
	}

	public boolean isNoDistanceCheck() {
		return noDistanceCheck;
	}

	public boolean isSpawned() {
		return spawned;
	}

	public boolean isUnderCombat() {
		return combat.underCombat();
	}

	@Override
	public boolean needMasksUpdate() {
		return super.needMasksUpdate() || nextTransformation != null
				|| changedCombatLevel || changedName;
	}

	@Override
	public void processEntity() {
		super.processEntity();
		processNPC();
	}

	public void processNPC() {
		if (isDead() || locked)
			return;
		if (!combat.process()) {
			if (!isForceWalking()) {
				if (!cantInteract) {
					if (!checkAgressivity()) {
						if (getFreezeDelay() < Utils.currentTimeMillis()) {
							if (((hasRandomWalk()) && World.getRotation(
									getPlane(), getX(), getY()) == 0)
									&& Math.random() * 1000.0 < 100.0) {
								int moveX = (int) Math
										.round(Math.random() * 10.0 - 5.0);
								int moveY = (int) Math
										.round(Math.random() * 10.0 - 5.0);
								resetWalkSteps();
								if (getMapAreaNameHash() != -1) {
									if (!MapAreas.isAtArea(
											getMapAreaNameHash(), this)) {
										forceWalkRespawnTile();
										return;
									}
									addWalkSteps(getX() + moveX,
											getY() + moveY, 5);
								} else
									addWalkSteps(respawnTile.getX() + moveX,
											respawnTile.getY() + moveY, 5);
							}
						}
					}
				}
			}
		}
		if (id == 5920)
			setRandomWalk(true);
		if (id == 5919)
			setRandomWalk(true);
		if (id == 7753)
			setRandomWalk(false);
		if (id == 1917)
			setRandomWalk(false);
		if (id == 585)
			setRandomWalk(false);
		if (id == 587)
			setRandomWalk(false);
		if (id == 538)
			setRandomWalk(false);
		if (id == 2998)
			setRandomWalk(false);
		if (id == 683)
			setRandomWalk(false);
		if (id == 6970)
			setRandomWalk(false);
		if (id == 519)
			setRandomWalk(false);
		if (id == 549)
			setRandomWalk(false);
		if (id == 11699)
			setRandomWalk(false);
		if (id == 550)
			setRandomWalk(false);
		if (id == 7935)
			setRandomWalk(false);
		if (id == 548)
			setRandomWalk(false);
		if (id == 598)
			setRandomWalk(false);
		if (id == 13727)
			setRandomWalk(false);
		if (id == 2253)
			setRandomWalk(true);
		if (id == 8462)
			setRandomWalk(false);
		if (id == 13697)
			setRandomWalk(true);
		if (id == 455)
			setName("Herbist of Xiduth");
		if (id == 7412)
			setName("Trivia-bunny");
		if (id == 1917)
			setName("Player-shop Manager");
		if (id == 7411)
			setName("Skilling-bunny");
		if (id == 15558)
			setName("Commander Origin");
		if (id == 6524)
			setName("Bob The Decanter");
		if (id == 15172)
			setName("Jordan");
		if (id == 15173)
			setName("Detail");
		if (id == 7753)
			setName("Oracle of Xiduth");
		if (isForceWalking()) {
			if (getFreezeDelay() < Utils.currentTimeMillis()) {
				if (getX() != forceWalk.getX() || getY() != forceWalk.getY()) {
					if (!hasWalkSteps())
						addWalkSteps(forceWalk.getX(), forceWalk.getY(),
								getSize(), true);
					if (!hasWalkSteps()) {
						setNextWorldTile(new WorldTile(forceWalk));
						forceWalk = null;
					}
				} else
					forceWalk = null;
			}
		}
	}

	public void removeTarget() {
		if (combat.getTarget() == null)
			return;
		combat.removeTarget();
	}

	@Override
	public void reset() {
		super.reset();
		setDirection(getRespawnDirection());
		combat.reset();
		bonuses = NPCBonuses.getBonuses(id);
		forceWalk = null;
	}

	@Override
	public void resetMasks() {
		super.resetMasks();
		nextTransformation = null;
		changedCombatLevel = false;
		changedName = false;
	}

	public void sendCharms(Player player) {
		if (Utils.random(3) != 0)
			dropCharm(player, CHARMS[Utils.random(CHARMS.length)]);
		dropCharm(player, new Item(995, Utils.random(30000)));
		player.setPvMPoints(player.getPvMPoints() + Utils.random(3));
	/*	player.sendMessage("<col=" + ChatColors.RED + ">You now have "
				+ player.getPvMPoints() + " PvM points."); */
	}

	@Override
	public void sendDeath(Entity source) {
		final NPCCombatDefinitions defs = getCombatDefinitions();
		resetWalkSteps();
		combat.removeTarget();
		setNextAnimation(null);
		WorldTasksManager.schedule(new WorldTask() {
			int loop;

			@Override
			public void run() {
				if (loop == 0) {
					setNextAnimation(new Animation(defs.getDeathEmote()));
				} else if (loop >= defs.getDeathDelay()) {
					drop();
					reset();
					setLocation(respawnTile);
					finish();
					if (!isSpawned())
						setRespawnTask();
					stop();
				}
				loop++;
			}
		}, 0, 1);
	}

	public void sendDrop(Player player, Drop drop) {
		int size = getSize();
		String dropName = ItemDefinitions.getItemDefinitions(drop.getItemId())
				.getName().toLowerCase();
		Item item = ItemDefinitions.getItemDefinitions(drop.getItemId())
				.isStackable() ? new Item(drop.getItemId(),
				(drop.getMinAmount() * Settings.DROP_RATE)
						+ Utils.getRandom(drop.getExtraAmount()
								* Settings.DROP_RATE)) : new Item(
				drop.getItemId(), drop.getMinAmount()
						+ Utils.getRandom(drop.getExtraAmount()));
		sendCharms(player);
		if (getId() == 2783) {
			player.setPvMPoints(player.getPvMPoints() + 3);
			player.sendMessage("<col=" + ChatColors.RED
					+ ">You have defeated a Dark beast for bonus points.");
		}
		if (getId() == 13450) {
			player.setPvMPoints(player.getPvMPoints() + 125);
			player.sendMessage("<col=" + ChatColors.RED
					+ ">You have defeated Nex.");
		}
		int glacor = 14301;
		ArrayList<Integer> stuffs = new ArrayList<Integer>();
		stuffs.add(6230);
		stuffs.add(6231);
		stuffs.add(6229);
		stuffs.add(6232);
		stuffs.add(6240);
		stuffs.add(6241);
		stuffs.add(6242);
		stuffs.add(6233);
		stuffs.add(6234);
		stuffs.add(6245);
		stuffs.add(6246);
		stuffs.add(6238);
		stuffs.add(6239);
		stuffs.add(6227);
		stuffs.add(6625);
		stuffs.add(6223);
		stuffs.add(6222);
		if (stuffs.indexOf(getId()) != -1) {
			player.getInventory().addItem(21776, 1);
			player.sm("You receive some shards.");
		}
		ArrayList<Integer> elite = new ArrayList<Integer>();
		elite.add(8312);
		elite.add(8313);
		elite.add(8314);
		elite.add(8315);
		elite.add(8316);
		elite.add(8317);
		elite.add(8318);
		elite.add(8319);
		elite.add(8320);
		elite.add(8321);
		elite.add(8322);
		elite.add(8323);
		elite.add(8324);
		elite.add(8325);
		elite.add(8326);
		elite.add(8327);
		elite.add(8328);
		elite.add(8329);
		elite.add(8330);
		if (elite.indexOf(getId()) != -1) {
			player.setPvMPoints(player.getPvMPoints() + 8);
			player.getInventory().addItem(995, Utils.random(55000));
			player.getMoneyPouch().add(player.getInventory().getIntegerOf(995));
			player.sendMessage("<col="
					+ ChatColors.PURPLE
					+ ">You have defeated an elite npc for bonus points and coins.");
		}
		if (getId() == 15174) {
		player.setPvMPoints(player.getPvMPoints() + Utils.random(50));
		player.getInventory().addItem(995, Utils.random(150000));
		player.getMoneyPouch().add(player.getInventory().getIntegerOf(995));
			player.sendMessage("<col=" + ChatColors.PURPLE
					+ ">You have defeated an elite boss for bonus points and loot!");
		}
		if (getId() == 15172) {
				player.setPvMPoints(player.getPvMPoints() + Utils.random(50));
				player.getInventory().addItem(995, Utils.random(150000));
				player.getMoneyPouch().add(player.getInventory().getIntegerOf(995));
					player.sendMessage("<col=" + ChatColors.PURPLE
							+ ">You have defeated an elite boss for bonus points and loot!");
		}
		if (getId() == 11872) {
			player.setPvMPoints(player.getPvMPoints() + Utils.random(250));
		player.getInventory().addItem(995, Utils.random(400000));
		player.getMoneyPouch().add(player.getInventory().getIntegerOf(995));
			player.sendMessage("<col=" + ChatColors.PURPLE
					+ ">You have defeated an elite boss for bonus points and loot!");
		}
		if (getId() == glacor) {
			player.setPvMPoints(player.getPvMPoints() + 8);
			player.getInventory().addItem(995, Utils.random(75000));
			player.getMoneyPouch().add(player.getInventory().getIntegerOf(995));
			player.sendMessage("<col=" + ChatColors.RED
					+ ">You have defeated a Glacor for bonus points and coins.");
		}
		if (getId() == 941) {
			player.setPvMPoints(player.getPvMPoints() + Utils.random(8));
			player.getInventory().addItem(995, Utils.random(37000));
			player.getMoneyPouch().add(player.getInventory().getIntegerOf(995));
			player.sendMessage("<col=" + ChatColors.RED
					+ ">You have defeated a Green dragon for bonus points.");
		}
		if (getId() == 1265) {
			player.setPvMPoints(player.getPvMPoints() + Utils.random(3));
			player.getInventory().addItem(995, Utils.random(10000));
			player.getMoneyPouch().add(player.getInventory().getIntegerOf(995));
			player.sendMessage("<col=" + ChatColors.RED
					+ ">You have defeated a Rock crab for bonus points.");
		}
		if (getId() == 117) {
			player.setPvMPoints(player.getPvMPoints() + Utils.random(3));
			player.getInventory().addItem(995, Utils.random(7000));
			player.getMoneyPouch().add(player.getInventory().getIntegerOf(995));
			player.sendMessage("<col=" + ChatColors.RED
					+ ">You have defeated a Hill giant for bonus points.");
		}
		if (getId() == 84) {
			player.setPvMPoints(player.getPvMPoints() + Utils.random(3));
			player.getInventory().addItem(995, Utils.random(17000));
			player.getMoneyPouch().add(player.getInventory().getIntegerOf(995));
			player.sendMessage("<col=" + ChatColors.RED
					+ ">You have defeated a Black demon for bonus points.");
		}
		if (getId() == 8777) {
			player.setPvMPoints(player.getPvMPoints() + Utils.random(3));
			player.getInventory().addItem(995, Utils.random(17000));
			player.getMoneyPouch().add(player.getInventory().getIntegerOf(995));
			player.sendMessage("<col=" + ChatColors.RED
					+ ">You have gained bonus points and gold.");
		}

		if (getId() == 8776) {
			player.setPvMPoints(player.getPvMPoints() + Utils.random(3));
			player.getInventory().addItem(995, Utils.random(17000));
			player.getMoneyPouch().add(player.getInventory().getIntegerOf(995));
			player.sendMessage("<col=" + ChatColors.RED
					+ ">You have gained bonus points and gold.");
		}

		if (getId() == 51) {
			player.setPvMPoints(player.getPvMPoints() + Utils.random(1, 8));
			player.addfdragonkills();
			player.sendMessage("<col=" + ChatColors.RED
					+ ">You have defeated a Frost dragon for bonus points.");
			player.sendMessage("<col=" + ChatColors.BLUE + ">You now have "
					+ player.getfdragonkills() + " Frost dragon points.");
		}
		if (getId() == 15222) {
			player.getInventory().addItem(16, 1);
			player.getInventory().addItem(990, 5);
		}
		if (getId() == 15172) {
			player.setPvMPoints(player.getPvMPoints() + 200);
			player.sendMessage("<col=" + ChatColors.RED
					+ ">You have received 200 PvM points for killing one of Sunfreet's servants.");
		}
		if (getId() == 15174) {
			player.setPvMPoints(player.getPvMPoints() + 200);
			player.sendMessage("<col=" + ChatColors.RED
					+ ">You have received 200 PvM points for killing one of Sunfreet's servants.");
		}
		if (getId() == 12878) {
			player.setPvMPoints(player.getPvMPoints() + 200);
			player.sendMessage("<col=" + ChatColors.RED
					+ ">You have defeated Blink.");
		}

		/*
		 * if (getId() == isBoss) { player.setPvMPoints(player.getPvMPoints() +
		 * 250); player.sendMessage("<col=" + ChatColors.RED + ">You now have "
		 * + player.getPvMPoints() + " PvM points."); }
		 */
		/**
		 * 
		 * Start of lootshare.
		 * 
		 */
		FriendChatsManager fc = player.getCurrentFriendChat();
		if (player.lootshareEnabled()) {
			if (fc != null) {
				CopyOnWriteArrayList<Player> players = fc.getPlayers();
				CopyOnWriteArrayList<Player> playersWithLs = new CopyOnWriteArrayList<Player>();
				for (Player p : players) {
					if (p.lootshareEnabled()
							&& p.getRegionId() == player.getRegionId()) // If
																		// players
																		// in FC
																		// have
																		// LS
																		// enabled
																		// and
																		// are
																		// also
																		// in
																		// the
																		// same
																		// map
																		// region.
						playersWithLs.add(p);
				}
				Player luckyPlayer = playersWithLs
						.get((int) (Math.random() * playersWithLs.size())); // Choose
																			// a
																			// random
																			// player
																			// to
																			// get
																			// the
																			// drop.
				World.addGroundItem(item, new WorldTile(getCoordFaceX(size),
						getCoordFaceY(size), getPlane()), luckyPlayer, false,
						180, true);
				luckyPlayer.sendMessage(String.format(
						"<col=115b0d>You received: %sx %s.</col>",
						item.getAmount(), dropName));
				for (Player p : playersWithLs) {
					if (!p.equals(luckyPlayer))
						p.sendMessage(String.format("%s received: %sx %s.",
								luckyPlayer.getDisplayName(), item.getAmount(),
								dropName));
				}
				return;
			}
		}
		/* End of LootShare */

		if (this.getId() == 4291 && drop.getItemId() == 8844) {
			WGuildControler.dropDefender(player, this);
			return;
		}
		World.addGroundItem(item, new WorldTile(getCoordFaceX(size),
				getCoordFaceY(size), getPlane()), player, false, 180, true);
		if (dropName.contains("pernix") || dropName.contains("torva")
				|| dropName.contains("virtus") || dropName.contains("bandos")
				|| dropName.contains("hilt") || dropName.contains("divine")
			//	|| dropName.contains("saradomin")
				|| dropName.contains("visage") || dropName.contains("spectral")
				|| dropName.contains("elysian")
				|| dropName.contains("steadfast")
				|| dropName.contains("armadyl chest")
				|| dropName.contains("armadyl plate")
				|| dropName.contains("armadyl boots")
				|| dropName.contains("armadyl helmet")
				|| dropName.contains("armadyl gloves")
				|| dropName.contains("whip") || dropName.contains("buckler")
				|| dropName.contains("glaiven") || dropName.contains("tax")
				|| dropName.contains("ragefire")
				|| dropName.contains("spirit shield")
				|| dropName.contains("arcane") || dropName.contains("chaotic")
				|| dropName.contains("cannon")
				|| dropName.contains("dragon pickaxe")
				|| dropName.contains("(deg)")) {
			World.sendWorldMessage(
					"<col=ff8c38><img=7>News: " + player.getDisplayName()
							+ " has just recieved a " + dropName + " drop!"
							+ "</col> ", false);
		}
	}

	public void sendSoulSplit(final Hit hit, final Entity user) {
		final NPC target = this;
		if (hit.getDamage() > 0)
			World.sendProjectile(user, this, 2263, 11, 11, 20, 5, 0, 0);
		user.heal(hit.getDamage() / 5);
		WorldTasksManager.schedule(new WorldTask() {
			@Override
			public void run() {
				setNextGraphics(new Graphics(2264));
				if (hit.getDamage() > 0)
					World.sendProjectile(target, user, 2263, 11, 11, 20, 5, 0,
							0);
			}
		}, 1);
	}

	@Override
	public void setAttackedBy(Entity target) {
		super.setAttackedBy(target);
		if (target == combat.getTarget()
				&& !(combat.getTarget() instanceof Familiar))
			lastAttackedByTarget = Utils.currentTimeMillis();
	}

	public void setBonuses(int[] bonuses) {
		this.bonuses = bonuses;
	}

	public void setCanBeAttackFromOutOfArea(boolean b) {
		canBeAttackFromOutOfArea = b;
	}

	public void setCantFollowUnderCombat(boolean canFollowUnderCombat) {
		this.cantFollowUnderCombat = canFollowUnderCombat;
	}

	public void setCantInteract(boolean cantInteract) {
		this.cantInteract = cantInteract;
		if (cantInteract)
			combat.reset();
	}

	public void setCapDamage(int capDamage) {
		this.capDamage = capDamage;
	}

	public void setCombatLevel(int level) {
		combatLevel = getDefinitions().combatLevel == level ? -1 : level;
		changedCombatLevel = true;
	}

	public void setForceAgressive(boolean forceAgressive) {
		this.forceAgressive = forceAgressive;
	}

	public void setForceFollowClose(boolean forceFollowClose) {
		this.forceFollowClose = forceFollowClose;
	}

	public void setForceMultiAttacked(boolean forceMultiAttacked) {
		this.forceMultiAttacked = forceMultiAttacked;
	}

	public void setForceTargetDistance(int forceTargetDistance) {
		this.forceTargetDistance = forceTargetDistance;
	}

	public void setForceWalk(WorldTile tile) {
		resetWalkSteps();
		forceWalk = tile;
	}

	/**
	 * Sets the locked.
	 * 
	 * @param locked
	 *            The locked to set.
	 */
	public void setLocked(boolean locked) {
		this.locked = locked;
	}

	public void setLureDelay(int lureDelay) {
		this.lureDelay = lureDelay;
	}

	public void setName(String string) {
		this.name = getDefinitions().name.equals(string) ? null : string;
		changedName = true;
	}

	public void setNextTimerbar(TimerBar timerbar) {
		Timerbar = timerbar;
	}

	public void setNoDistanceCheck(boolean noDistanceCheck) {
		this.noDistanceCheck = noDistanceCheck;
	}

	public void setNPC(int id) {
		this.id = id;
		bonuses = NPCBonuses.getBonuses(id);
	}

	public void setRandomWalk(boolean forceRandomWalk) {
		this.randomwalk = forceRandomWalk;
	}

	public void setRespawnTask() {
		if (!hasFinished()) {
			reset();
			setLocation(respawnTile);
			finish();
		}
		CoresManager.slowExecutor.schedule(new Runnable() {
			@Override
			public void run() {
				try {
					spawn();
				} catch (Throwable e) {
					Logger.handle(e);
				}
			}
		}, getCombatDefinitions().getRespawnDelay() * 600,
				TimeUnit.MILLISECONDS);
	}

	public void setSpawned(boolean spawned) {
		this.spawned = spawned;
	}

	public void setTarget(Entity entity) {
		if (isForceWalking()) // if force walk not gonna get target
			return;
		combat.setTarget(entity);
		lastAttackedByTarget = Utils.currentTimeMillis();
	}

	public void spawn() {
		setFinished(false);
		World.addNPC(this);
		setLastRegionId(0);
		World.updateEntityRegion(this);
		loadMapRegions();
		checkMultiArea();
	}

	@Override
	public String toString() {
		return getDefinitions().name + " - " + id + " - " + getX() + " "
				+ getY() + " " + getPlane();
	}

	public void transformIntoNPC(int id) {
		setNPC(id);
		nextTransformation = new Transformation(id);
	}

	public boolean withinDistance(Player tile, int distance) {
		return super.withinDistance(tile, distance);
	}

	public void animate(int anim) {
		setNextAnimation(new Animation(anim));
		
	}
}
